﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Pixysoft.Framework.Reports.Core;

namespace Pixysoft.Framework.Reports.Controller
{
    class ReportTableController
    {
        List<string> columnNames = new List<string>();

        Dictionary<string, string[]> buffer = new Dictionary<string, string[]>();

        public IReportTable CreateReportTable(IReportSetting setting, DataTable table)
        {
            ReportTable report = new ReportTable();

            CreateReportColumns(setting.GetColumnSettings(), report, null, table);

            if (setting.GetRowSettings().Length == 0)
            {
                InitializeColumnNames(setting.GetColumnSettings());

                CreateReportCellsByColumns(setting, report, table);
            }
            else
            {
                CreateReportRows(setting, setting.GetRowSettings(), report, null, table);

                CreateReportCellsByRows(setting, report, table);
            }

            report.Caculate();

            return report;
        }

        private void CreateReportColumns(IReportColumnSetting[] columnSettings, ReportTable report, IReportColumn parent, DataTable table)
        {
            foreach (IReportColumnSetting columnSetting in columnSettings)
            {
                bool hasSuccessor = columnSetting.GetChildrenSettings().Length > 0;

                foreach (string headerName in GetGroupColumnHeaders(columnSetting, table))
                {
                    IReportColumn column = null;

                    if (hasSuccessor)
                    {
                        column = report.Columns.NewColumn(headerName, -1, columnSetting);

                        column.Parent = parent;

                        if (parent != null)
                            parent.Children.Add(column);

                        CreateReportColumns(columnSetting.GetChildrenSettings(), report, column, table);
                    }
                    else
                    {
                        column = report.Columns.NewColumn(headerName, report.Columns.Count, columnSetting);

                        column.Parent = parent;

                        if (parent != null)
                            parent.Children.Add(column);

                        column.Initialize();

                        report.Columns.Add(column);
                    }
                }
            }
        }

        private string[] GetGroupColumnHeaders(IReportColumnSetting columnSetting, DataTable table)
        {
            string columnName = columnSetting.ColumnName;

            string headerName = columnSetting.HeaderName;

            switch (columnSetting.ExpressionType)
            {
                case ExpressionType.Group:
                    {
                        columnName = columnName.Trim().ToUpper();

                        if (buffer.ContainsKey(columnName))
                            return buffer[columnName];

                        List<string> list = new List<string>();

                        foreach (DataRow row in table.Rows)
                        {
                            object rawvalue = row[columnName];

                            if (rawvalue == null)
                                continue;

                            string groupvalue = rawvalue.ToString();

                            if (!list.Contains(groupvalue))
                                list.Add(groupvalue);
                        }

                        list.Sort();

                        buffer.Add(columnName, list.ToArray());

                        return list.ToArray();
                    }
                case ExpressionType.Column:
                case ExpressionType.Plain:
                default:
                    {
                        return new string[] { headerName };
                    }
            }
        }

        private void InitializeColumnNames(IReportColumnSetting[] columnSettings)
        {
            foreach (IReportColumnSetting setting in columnSettings)
            {
                string columnname = setting.ColumnName.Trim().ToUpper();

                if (!columnNames.Contains(columnname))
                    columnNames.Add(columnname);

                if (setting.GetChildrenSettings().Length > 0)
                    InitializeColumnNames(setting.GetChildrenSettings());
            }
        }

        private void CreateReportCellsByColumns(IReportSetting setting, ReportTable report, DataTable table)
        {
            foreach (DataRow row in table.Rows)
            {
                string rowIdentifier = CreateRowIdentifier(row);

                IReportRow reportrow = report.Rows.GetRow(rowIdentifier);

                if (reportrow == null)
                {
                    reportrow = report.Rows.NewRow(null, rowIdentifier, null);
                    reportrow.Initialize(setting);
                    report.Rows.Add(reportrow);
                }

                foreach (DataColumn column in table.Columns)
                {
                    object value = row[column];

                    foreach (IReportCell cell in reportrow.Cells)
                    {
                        if (!cell.NeedCellValue(column.ColumnName, value))
                            continue;

                        string columnIdentifier = CreateColumnIdentifier(cell.Column, row);

                        if (!string.Equals(cell.Column.Identifier, columnIdentifier, StringComparison.OrdinalIgnoreCase))
                            continue;

                        cell.AddCellValue(column.ColumnName, value);
                    }
                }
            }
        }


        private void CreateReportRows(IReportSetting setting, IReportRowSetting[] rowSettings, ReportTable report, IReportRow parent, DataTable table)
        {
            foreach (IReportRowSetting rowSetting in rowSettings)
            {
                bool hasSuccessor = rowSetting.GetChildrenSettings().Length > 0;

                foreach (string headerName in GetGroupColumnHeaders(rowSetting, table))
                {
                    IReportRow row = null;

                    if (hasSuccessor)
                    {
                        row = report.Rows.NewRow(headerName, null, rowSetting);

                        row.Parent = parent;

                        if (parent != null)
                            parent.Children.Add(row);

                        CreateReportRows(setting, rowSetting.GetChildrenSettings(), report, row, table);
                    }
                    else
                    {
                        row = report.Rows.NewRow(headerName, null, rowSetting);

                        row.Parent = parent;

                        if (parent != null)
                            parent.Children.Add(row);

                        row.Initialize(setting);

                        report.Rows.Add(row);
                    }
                }
            }
        }

        private string[] GetGroupColumnHeaders(IReportRowSetting rowSetting, DataTable table)
        {
            string columnName = rowSetting.ColumnName;

            string headerName = rowSetting.HeaderName;

            switch (rowSetting.ExpressionType)
            {
                case ExpressionType.Group:
                    {
                        columnName = columnName.Trim().ToUpper();

                        if (buffer.ContainsKey(columnName))
                            return buffer[columnName];

                        List<string> list = new List<string>();

                        foreach (DataRow row in table.Rows)
                        {
                            object rawvalue = row[columnName];

                            if (rawvalue == null)
                                continue;

                            string groupvalue = rawvalue.ToString();

                            if (!list.Contains(groupvalue))
                                list.Add(groupvalue);
                        }

                        list.Sort();

                        buffer.Add(columnName, list.ToArray());

                        return list.ToArray();
                    }
                case ExpressionType.Column:
                case ExpressionType.Plain:
                default:
                    {
                        return new string[] { headerName };
                    }
            }
        }

        private void CreateReportCellsByRows(IReportSetting setting, ReportTable report, DataTable table)
        {
            foreach (IReportRow reportrow in report.Rows)
            {
                foreach (DataRow row in table.Rows)
                {
                    string rowIdentifier = CreateRowIdentifier(reportrow, row);

                    if (!string.Equals(reportrow.Identifier, rowIdentifier, StringComparison.OrdinalIgnoreCase))
                        continue;

                    foreach (DataColumn column in table.Columns)
                    {
                        object value = row[column];

                        foreach (IReportCell cell in reportrow.Cells)
                        {
                            if (!cell.NeedCellValue(column.ColumnName, value))
                                continue;

                            string columnIdentifier = CreateColumnIdentifier(cell.Column, row);

                            if (!string.Equals(cell.Column.Identifier, columnIdentifier, StringComparison.OrdinalIgnoreCase))
                                continue;

                            cell.AddCellValue(column.ColumnName, value);
                        }
                    }
                }
            }
        }


        private string CreateRowIdentifier(DataRow row)
        {
            StringBuilder builder = new StringBuilder();
            foreach (string columnName in columnNames)
            {
                builder.Append(row[columnName]);
            }
            return builder.ToString().Trim().ToUpper();
            //return Pixysoft.Security.MD5.GetMD5(builder.ToString());
        }

        private string CreateRowIdentifier(IReportRow reportrow, DataRow row)
        {
            StringBuilder builder = new StringBuilder();
            foreach (string columnName in reportrow.ColumnNames)
            {
                builder.Append(row[columnName]);
            }
            return builder.ToString().Trim().ToUpper();
            //return Pixysoft.Security.MD5.GetMD5(builder.ToString());
        }

        private string CreateColumnIdentifier(IReportColumn reportcolumn, DataRow row)
        {
            StringBuilder builder = new StringBuilder();
            foreach (string columnName in reportcolumn.ColumnNames)
            {
                builder.Append(row[columnName]);
            }
            return builder.ToString().Trim().ToUpper();
            //return Pixysoft.Security.MD5.GetMD5(builder.ToString());
        }
    }
}
